package cn.shutdown.demo.jvm.trace;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * -XX:+TraceClassUnloading -XX:+TraceClassLoading
 * <p>
 * -verbose:class
 *
 * @author Dmn
 */
public class UnloadClass implements Opcodes {
    public static void main(String args[]) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        //ClassWriter 以字节码的形式生成类的类访问器, 参数
        // ClassWriter.COMPUTE_MAXS 如果必须自动计算最大堆栈大小和局部变量数，则为true 。
        // ClassWriter.COMPUTE_FRAMES 如果堆栈映射帧必须从头开始重新计算。
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
        // 定义了一个 基于jdk1.7的 public类型的类，名为Example,继承于Object
        cw.visit(V1_7, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
        //定义了一个构造方法
        MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
        mw.visitVarInsn(ALOAD, 0);
        mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        mw.visitInsn(RETURN);
        mw.visitMaxs(0, 0);
        mw.visitEnd();

        // 生成main方法中的字节码指令
        mw = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
        //获取该方法
        mw.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
        //加载字符串参数
        mw.visitLdcInsn("Hello world!");
        //调用该方法
        mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
        mw.visitInsn(RETURN);
        mw.visitMaxs(0, 0);
        mw.visitEnd();
        //生成class文件对应的二进制流
        byte[] code = cw.toByteArray();
        System.out.println("\n\n================================================");
        for (int i = 0; i < 10; i++) {
            //创建类加载器
            UnloadClassLoader loader = new UnloadClassLoader();
            //获取了 ClassLoader类的 defineClass方法对象
            Method m = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
            //设置方法的访问权限为可访问
            m.setAccessible(true);
            //调用 loader对象的 defineClass方法
            //ClassLoader的defineClass方法的作用是：将字节数组转换为类Class的实例
            //这样就可以将 刚刚生成的class文件的二进制流加载并转化为Example类的实例
            m.invoke(loader, "Example", code, 0, code.length);
            m.setAccessible(false);
            System.gc();
        }
    }
}